﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using middleware.utiles;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using middleware.dominio.persistencia;
using middleware.dominio.persistencia.exception;
using middleware.dominio.negocio;
using middleware.dominio.negocio.exception;

namespace middleware.dominio
{
    public class ConsultasManager
    {
        private static readonly ConsultasManager instance = new ConsultasManager();

        private LogManager logger = LogManager.getInstance();

        private IMiddlewarePersistencia middlewarePersistencia = PersistenceFactory.getMiddlewarePersistencia(); 

        private string[] elementosConsulta = { "select", "from", "where", "groupby" };

        private int numElemWhere = 2;

        private int numElemGroupby = 3;

        private int numGroupSelect = 1;

        private int numGroupWhere = 3;

        private int numGroupGroupby = 4;

        public static ConsultasManager getInstance()
        {
            return instance;
        }

        public List<IConsulta> CargarConsultas(string directorio)
        {
            List<IConsulta> listaResultado = new List<IConsulta>();
            try
            {
                if (Directory.Exists(directorio))
                {
                    string[] listaDlls = Directory.GetFiles(directorio, "*.dll", SearchOption.TopDirectoryOnly);
                    if (listaDlls != null)
                    {
                        for (int i = 0; i < listaDlls.Length; i++)
                        {
                            Assembly assembly = Assembly.LoadFile(listaDlls[i]);

                            //Recorro para ver los tipos que puedo instanciar
                            foreach (Type tipo in assembly.GetTypes())
                            {
                                if (tipo.Name.Contains(@"Consulta")) //Para asegurarnos de instanciar solo las entradas
                                {
                                    //Creo la instancia del tipo de bandeja y lo agrego a la lista
                                    IConsulta consulta = (IConsulta)Activator.CreateInstance(tipo);
                                    listaResultado.Add(consulta);
                                }
                            }
                        }
                    }
                }
            }
            catch (FileNotFoundException e)
            {                
                throw new BusinessException(Middleware.ERROR_EJECUCION + "mientras se buscaban el directorio de las consultas", e);
            }
            catch (Exception e)
            {
                throw new BusinessException(Middleware.ERROR_EJECUCION + "mientras se cargaban las consultas", e);                
            }
            return listaResultado;
        }

        /*
         * Metodo que ejecuta la consulta dinamica, primero obtiene el matcher para conseguir las diferentes partes del sql, estas partes
         * serian las del select, el where, y el groupby.
         */
        public List<string[]> EjecutarConsulta(IConsulta consulta)
        {
            
            List<String[]> resultado = new List<string[]>();
            try
            {
                // Obtengo la consulta a parsear
                string sqlParsear = consulta.ObtenerConsulta();
                // Calculo la expresion regular necesaria para parsear el slq de la consulta
                string regex = this.obtenerExpresionRegular(sqlParsear);
                // Matcheo el sql con la expresion regular y obtengo los grupos de esta, que corresponden al select, where y groupby
                Match match = Regex.Match(sqlParsear, regex, RegexOptions.IgnoreCase);

                if (sqlParsear.Contains(elementosConsulta[numElemWhere]) && sqlParsear.Contains(elementosConsulta[numElemGroupby]))
                {
                    resultado = middlewarePersistencia.ObtenerEjecucionConsultaDinamica(match.Groups[numGroupSelect].Value, match.Groups[numGroupWhere].Value, match.Groups[numGroupGroupby].Value);
                }
                else if (sqlParsear.Contains(elementosConsulta[numElemWhere]))
                {
                    resultado = middlewarePersistencia.ObtenerEjecucionConsultaDinamica(match.Groups[numGroupSelect].Value, match.Groups[numGroupWhere].Value, "");
                }
                else if (sqlParsear.Contains(elementosConsulta[numElemGroupby]))
                {
                    resultado = middlewarePersistencia.ObtenerEjecucionConsultaDinamica(match.Groups[numGroupSelect].Value, "", match.Groups[numGroupGroupby].Value);
                }
                else
                {
                    resultado = middlewarePersistencia.ObtenerEjecucionConsultaDinamica(match.Groups[numGroupSelect].Value, "", "");
                }
            }
            catch (ConsultarPersisteciaException e)
            {
                throw new BusinessException(Middleware.ERROR_EJECUCION + "mientras se buscaban el directorio de las consultas", e);
            }
            catch (FormatException e)
            {
                throw new BusinessException(Middleware.ERROR_EJECUCION + "mientras se buscaban el directorio de las consultas", e);
            }

            return resultado;
        }

        /*
         * 
         * Metodo que se  encarga de armar el parser de una consulta sql teniendo en cuenta los elementos de consulta.
         * Termina formando algo del estilo select(.*)from(.*)where(.*)groupby(.*), dado lo que tenga el sql a parsear.
         * 
         */
        private string obtenerExpresionRegular(string sqlParsear)
        {
            string regex = @"";

            for (int i = 0; i < elementosConsulta.Length; i++)
            {
                if (sqlParsear.Contains(elementosConsulta[i]))
                {                    
                    regex = regex + elementosConsulta[i] + "(.*)";
                }
            }
            return regex;
        }
    }
}
